package implementation.server;

import java.util.ArrayList;
import java.util.Collection;


import java.util.Iterator;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;


import implementation.data.records.CForumRecord;
import implementation.data.records.CPostRecord;
import implementation.data.records.CThreadRecord;
import implementation.data.records.CUserRecord;
import implementation.data.records.ReadWriteGuard;





public class CDB extends ReadWriteGuard implements IDB {

	private ConcurrentMap <Integer,CUserRecord> _users;
	private ConcurrentMap <Integer,CForumRecord> _forums;
	private ConcurrentMap <Integer, CThreadRecord> _threads;
	private ConcurrentMap <Integer, CPostRecord> _posts;
	private int _userCount;
	private int _forumCount;
	private int _postsCount;
	private int _threadCount;
	
	private Object _monitor1;

	
	/**
	 * Class Constructor
	 * 
	 */
	public CDB(){
		super();
		this._users = new ConcurrentHashMap<Integer,CUserRecord>();
		this._forums = new ConcurrentHashMap<Integer,CForumRecord>();
		this._threads = new ConcurrentHashMap<Integer,CThreadRecord>();
		this._posts = new ConcurrentHashMap<Integer,CPostRecord>();
		this._userCount = 0;
		this._forumCount = 0;
		this._postsCount = 0;
		this._threadCount = 0;
		
		this._monitor1 = new Object();
		
		
		//Initialization
		addForum("Forum 1");
		addForum("Forum 2");
		addForum("Forum 3");
		addForum("Forum 4");
		addForum("Forum 5");
		addForum("Forum 6");
		addForum("Forum 7");
		addForum("Forum 8");
		addForum("Forum 9");
		addForum("Forum 10");
		addForum("Forum 11");
		addForum("Forum 12");
		addForum("Forum 13");
		addForum("Forum 14");
		addForum("Forum 15");
		
		
		
		addThread(1, "Maccabi", "Tel Aviv", 1);
		addPost(1, "Yes", "yes", 1);
		addPost(1, "No", "yes", 1);
		addPost(1, "Maybe", "yes", 1);
		addPost(1, "YesNo", "yes", 1);
		
		
		addUser("maxim", "123456");
		addUser("yuri", "123456");
		addUser("yuval", "123456");
		addUser("raz", "123456");
		
		follow(1, 2);
		follow(1, 3);
		follow(1, 4);
		
		follow(2, 1);
		follow(2, 3);
		follow(2, 4);			
	}
	
	@Override
	public  int addUser(String username, String password) {
		
		synchronized (this._monitor1){
		
			this._waitingWriters++;
			while(!this.allowWriters()){
				try{this._monitor1.wait();} catch(InterruptedException e){return -1;}
			}
			this._waitingWriters--;
			this._activeWriters++;
		
		
			//check if user name is available

			Collection<CUserRecord> tCo = this._users.values();
			Iterator<CUserRecord> tIter = tCo.iterator();
			
			boolean tAvailable = true;
			while (tAvailable & tIter.hasNext())
			{
				if (tIter.next().get_userName().equals(username))
					tAvailable = false;
				
			}
			
			if(!tAvailable)
			{
				this._activeWriters--;
				this._monitor1.notifyAll();
				return -1;
			}
			
		
			// 1. generate user ID
			int tUserId= _generateUserID();
		
			// 2. create new user
			CUserRecord tUserRecord= new CUserRecord(tUserId, username,password);
		
			// 3. store the new record in DB
			this._users.put(tUserId, tUserRecord);
		
			this._activeWriters--;
			this._monitor1.notifyAll();
		
			return tUserId;
		}
	}
	
	public int getUserID(String username){
		synchronized(this._monitor1){
			this._waitingReaders++;
			while(!this.allowReader())
				try{this._monitor1.wait();} catch(InterruptedException e){return -1;}
		
				this._waitingReaders--;
				this._activeReaders++;
			
				Collection<CUserRecord> tCo = this._users.values();
				Iterator<CUserRecord> tIter = tCo.iterator();
				
				boolean tAvailable = true;
				int tUserId = -1;
				while (tAvailable & tIter.hasNext())
				{
					
					CUserRecord tUserRecord = tIter.next();
					if (tUserRecord.get_userName().equals(username))
					{
						tAvailable = false;
						tUserId = tUserRecord.get_userId();
					}
				}
		
				this._activeReaders--;
				this._monitor1.notifyAll();

				return tUserId;
		}
	}
	

	@Override
	public IReadOnlyUser getUser(int userId){
		synchronized(this._monitor1){
			this._waitingReaders++;
			while(!this.allowReader())
				try{this._monitor1.wait();} catch(InterruptedException e){return null;}
		
				this._waitingReaders--;
				this._activeReaders++;
		
				CUserRecord tUserRecord = this._users.get(userId);
		
				this._activeReaders--;
				this._monitor1.notifyAll();
		
				return tUserRecord.clone();

		}
	}

	@Override
	public boolean follow(int followerID, int followedID)  {
			
		synchronized(this._monitor1){
			boolean tAnswer;
		
			this._waitingWriters++;
			while(!this.allowWriters()){
				try{this._monitor1.wait();} catch(InterruptedException e){return false;}
			}
			this._waitingWriters--;
			this._activeWriters++;
		
			tAnswer = this._users.get(followerID).addFriend(followedID); 
		
			this._activeWriters--;
			this._monitor1.notifyAll();
		
			return tAnswer;
		}
	}

	@Override
	public boolean stopFollowing(int userID, int friendID)  {
		
		synchronized(this._monitor1){
			boolean tAnswer;
		
			this._waitingWriters++;
			while(!this.allowWriters()){
				try{this._monitor1.wait();} catch(InterruptedException e){return false;}
			}
			this._waitingWriters--;
			this._activeWriters++;
		
			tAnswer = this._users.get(userID).removeFriend(friendID);
			this._activeWriters--;
			this._monitor1.notifyAll();
		
			return tAnswer;
		}
		
	}

	@Override
	public int addForum(String forumName) {
		int tForumId = _generateForumID();
		this._forums.put(tForumId ,new CForumRecord(tForumId, forumName));
		return tForumId;
	}

	@Override
	public int addThread(int forumId, String threadTopic, String threadContent, int authorID) throws NullPointerException{
		
		int tThreadId = _generateThreadId(); 
		CThreadRecord tCThreadRecord = new CThreadRecord(tThreadId, threadTopic, threadContent, authorID,forumId);
		this._threads.put(tThreadId, tCThreadRecord);
		this._forums.get(forumId).addThread(tThreadId);
		
		return tThreadId;
		
	}

	@Override
	public int addPost(int threadID, String postTopic, String postcontent, int authorID) throws NullPointerException {
		
		int tPostId = _generatePostId();
		int forumID = this.getThread(threadID).getForumID();
		CPostRecord tPostRecord = new CPostRecord(tPostId, postTopic, postcontent, authorID, threadID,forumID);
		
		this._posts.put(tPostId, tPostRecord);
		this._threads.get(threadID).addPost(tPostId);
		
		return tPostId;
	}

	@Override
	public IReadOnlyForum getForum(int forumID) {
		
		CForumRecord tForumRecord = this._forums.get(forumID); 
		return tForumRecord.clone();
	}

	@Override
	public ArrayList<IReadOnlyForum> getForums() {
		
		ArrayList<IReadOnlyForum> tAnswer= new ArrayList<IReadOnlyForum>();
		Set<Integer> tForumsID = this._forums.keySet();
		java.util.Iterator<Integer> iter = tForumsID.iterator();
		
		while (iter.hasNext())
			tAnswer.add(getForum((int)iter.next()));
		return tAnswer;
	}

	@Override
	public IReadOnlyThread getThread(int threadID) {
		
		CThreadRecord tThreadRecord = this._threads.get(threadID); 

		return tThreadRecord.clone();
	}

	@Override
	public IReadOnlyPost getPost(int postID) {
		CPostRecord tPostRecord = this._posts.get(postID);
		return tPostRecord.clone();
	}

	private int _generateUserID(){
		this._userCount++;
		return this._userCount;
	}
	
	private synchronized int _generateForumID(){
		this._forumCount++;
		return this._forumCount;
	}
	
	private synchronized int _generateThreadId(){
		this._threadCount++;
		return this._threadCount;
	}
	
	private synchronized int _generatePostId(){
		this._postsCount++;
		return this._postsCount;
	}



	@Override
	public boolean removeForum(int forumId) {
		
		this._forums.remove(forumId);
		
		//i need to delete all associated threads and posts
		
		
		return true;
	}

	@Override
	public boolean removeThread(int threadId) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean removePost(int postId) {
		return this._posts.remove(postId) != null;
	}

	@Override
	public boolean editPost(int postID, String topic, String content) {
		CPostRecord tPostRecord = this._posts.get(postID);
		if (tPostRecord == null) {
			return false;
		}
		tPostRecord.editPostContent(content);
		tPostRecord.editPostTopic(topic);
		return true;
	}
}
